#!/usr/bin/env python
# -*- coding: utf_8 -*-

import gtk
import gtk.glade
import pango

import gnomevfs 

import os
import pickle

from xdg.DesktopEntry import DesktopEntry

import utils.IconFactory as uspIconFactory
import utils.ButtonFactory as uspButtonFactory
import utils.GConfigurator as uspGConfig
import utils.MenuFactory as MenuFactory
import utils.OptionDialog as uspOptionDialog

import uspCoreUtils

class PersistentList(object):
	@staticmethod
	def createNewListWith(file, list):
		try:
			fd = open(file, "wb")
		except IOError:
			raise Exception("Can't open file for writing!")
		else:
			pickle.dump(list, fd)
			fd.close()
			
	def __init__(self, pathToList):
		if not os.path.exists(pathToList):
			self.createNew(pathToList)
		
		if not os.path.isfile(pathToList):
			raise Exception("Path '"+ pathToList +"' is not a file!")
		
		self.pathToList = pathToList
	
	def createNew(self, pathToList):
		try:
			file = open(pathToList, "wb")
		except IOError:
			raise Exception("Can't open file for writing!")
		else:
			pickle.dump([], file)
			file.close()
			
	def readList(self):
		listToReturn = []
		try:
			file = open(self.pathToList, "rb")
		except IOError:
			raise Exception("Can't open file for reading!")
		else:
			listToReturn = pickle.load(file)
			file.close()
		return listToReturn
		
	def writeList(self, inList):
		if not isinstance(inList, list):
			raise Exception("Given object is not a list!")
		try:
			file = open(self.pathToList, "wb")
		except IOError:
			raise Exception("Can't open file for writing!")
		else:
			pickle.dump(inList, file)
			file.close()

class Task:
	def __init__(self, taskName):
		self.taskName = taskName
		self.desktopEntryFileNames = []
		self.numberOfColumns = 2
		
	def setName(self, newTaskName):
		self.taskName = newTaskName
	
	def getName(self):
		return self.taskName
	
	def getNumberOfColumns(self):
		return self.numberOfColumns
		
	def setNumberOfColumns(self, columns):
		self.numberOfColumns = columns
		
	def getDesktopEntries(self, rootDirectory):
		desktopEntries = []
		for fileName in self.desktopEntryFileNames:
			path = rootDirectory + "/" + fileName
			if not os.path.exists(path):
				raise Exception, "DesktopFile "+fileName+" doesn't exist."
			desktopEntry = DesktopEntry(path)
			desktopEntries.append(desktopEntry)
		return desktopEntries
		
	def addDesktopEntryFileName(self, element):
		self.desktopEntryFileNames.append(element)
	
	def getDesktopEntryFileNames(self):
		return self.desktopEntryFileNames
	
	def removeDesktopEntryFileNames(self, position):
		del(self.desktopEntryFileNames[position])

class MainNoteBookView(object):
	
	dragAndDropTargets = [("STRING", gtk.TARGET_SAME_APP , 0), ("text/uri-list", 0, 1), ("text/plain", 0, 2)]
	
	def __init__(self, noteBook, widgetTree, mainEventWindow):
		self.noteBook = noteBook
		self.mainEventWindow = mainEventWindow
		self.toolTips = gtk.Tooltips()
		self.iconFactory = uspIconFactory.IconFactory()
		
		self.dragging = False
		
		self.favouritesFolder = self.ensureFavouritesFolder()
		if not os.path.exists(self.favouritesFolder + "/task.list"):
			self.tasksList = PersistentList(self.favouritesFolder + "/task.list")
			tasks = [Task("default")]
			self.tasksList.writeList(tasks)
		else:	
			self.tasksList = PersistentList(self.favouritesFolder + "/task.list")
		
		self.noteBook.connect("switch-page", self.onPageSwitched)
		self.noteBook.connect("page-reordered", self.onPageReordered)
		
		self.currentPageIndex = 0
		
		self.fillNotebook()
		
	
	def getToplevelWindow(self):
		return self.mainEventWindow.get_toplevel()
		
	def ensureFavouritesFolder(self):
		homeFolder = os.path.expanduser("~")
		favouritesFolder = homeFolder + "/.usp2/tasks"
		
		if not os.path.exists(homeFolder + "/.usp2"):
			os.mkdir(homeFolder + "/.usp2")
			
		if not os.path.exists(favouritesFolder):
			os.mkdir(favouritesFolder)
			
		if not os.path.isdir(favouritesFolder):
			raise Exception(favouritesFolder + "is not a directory!")
		
		return favouritesFolder
	
	def clearNotebook(self):
		uspCoreUtils.clearWidgetChildren(self.noteBook)
		
	def fillNotebook(self):
		tasks = self.tasksList.readList()
		for task in tasks:
			self.createPage(task)
			
	def createPage(self, task):
		scrolledWindow = gtk.ScrolledWindow()
		scrolledWindow.set_shadow_type(gtk.SHADOW_NONE)
		scrolledWindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
		scrolledWindow.connect("drag_data_received", self.onDragDataRecieved)
		scrolledWindow.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.dragAndDropTargets, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
		self.noteBook.add(scrolledWindow)
		
		viewPort = gtk.Viewport()
		viewPort.set_shadow_type(gtk.SHADOW_NONE)
		scrolledWindow.add(viewPort)
		
		vbox = gtk.VBox()
		viewPort.add(vbox)
		
		table = gtk.Table(rows=1, columns=1, homogeneous=True)
		vbox.pack_start(table, False, False, 0)
		
		#add label
		pageIndex = self.noteBook.get_n_pages() - 1
		page = self.noteBook.get_nth_page(pageIndex)
		
		eventBox = gtk.EventBox()
		eventBox.set_visible_window(False)
		eventBox.connect("button_press_event", self.onTabLabelClicked, page)
		
		label = gtk.Label(task.getName())
		eventBox.add(label)
		
		eventBox.show_all()
		
		self.noteBook.set_show_tabs(1)
		self.noteBook.set_tab_label(page, eventBox)
		self.noteBook.set_tab_reorderable(page, True)
		
		page.associatedTable = table
		
		scrolledWindow.show_all()
		
		self.updatePage(pageIndex)
		
	def onTabLabelClicked(self, widget, event, page):
		self.noteBook.set_current_page(self.noteBook.page_num(page))
		if event.button == 3:
			menu = (
				("add..", self.onTabAddClicked), 
				("remove..", self.onTabRemoveClicked),
				("edit..", self.onTabEditClicked)
			)
			
			gtkMenuFactory = MenuFactory.GtkMenuFactory(menu)
			gtkMenuFactory.popup(event)
			
		
	def updatePage(self, index):
		page = self.noteBook.get_nth_page(index)
		table = page.associatedTable
		tasks = self.tasksList.readList()
		task = tasks[index]
		uspCoreUtils.clearWidgetChildren(table)
		table.resize(1,1)
		
		desktopEntries = task.getDesktopEntries(self.favouritesFolder)
		
		buttonFactory = uspButtonFactory.ButtonFactory()
		buttonFactory.iconSize = gtk.ICON_SIZE_DND
		buttonFactory.labelAlignment = 0.5
		buttonFactory.minLabelWidth = 10
		buttonFactory.toolTipWidget = self.toolTips
		buttonFactory.clearLabelAttributes()
		buttonFactory.addLabelAttribute(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1))
		buttonFactory.addLabelAttribute(pango.AttrWeight(pango.WEIGHT_NORMAL, 0, -1))
		
		if not hasattr(task, 'numberOfColumns'):
			task.setNumberOfColumns(2)
			self.tasksList.writeList(tasks)
			
		columns = task.getNumberOfColumns()
		
		for column in xrange(columns):
			inc = 0
			filteredDesktopEntries = [(index, entry) for index, entry in enumerate(desktopEntries) if index % columns == column]
			for position, desktopEntry in filteredDesktopEntries:
				labels = [desktopEntry.getName(), desktopEntry.getGenericName()]
				toolTipText = desktopEntry.getComment()
				buttonFactory.addEvent("button_release_event", self.onEntryButtonClicked, desktopEntry)
				button = buttonFactory.create(labels, desktopEntry.getIcon(), toolTipText)
				
				button.desktopEntry = desktopEntry
				button.position = position
				
				button.connect("drag_data_get", self.onDragDataSend, None)
				button.drag_source_set(gtk.gdk.BUTTON1_MASK, self.dragAndDropTargets, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
				
				button.connect("drag_data_received", self.onDragDataRecieved)
				button.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.dragAndDropTargets, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
				
				button.connect("drag_end", self.onDragEnd)
				
				button.connect("drag_begin", self.onDragBegin)
			
				table.attach(button, column, column+1, inc, inc+1)
				inc = inc + 1
				
	def onEntryButtonClicked(self, widget, event, desktopEntry):
		if event.button == 1:
			if self.dragging:
				return
			executeString = desktopEntry.getExec()
			executeArgs = uspCoreUtils.removeArgs(executeString.split())
			uspCoreUtils.execute(executeArgs)
			Configuration().hideView()
		elif event.button == 3:
			menu = (
				('remove', self.menuRemoveEntry, widget.position),
				('edit', self.menuEditEntry, desktopEntry)
			)
			
			gtkMenuFactory = MenuFactory.GtkMenuFactory(menu)
			gtkMenuFactory.popup(event)
			
	def menuEditEntry(self, widget, desktopEntry):
		dialog = uspOptionDialog.OptionDialog("Edit..", self.getToplevelWindow())
		values = [
			uspOptionDialog.StringOption("Name", desktopEntry.getName()),
			uspOptionDialog.StringOption("GenericName", desktopEntry.getGenericName()),
			uspOptionDialog.StringOption("Comment", desktopEntry.getComment()),
			uspOptionDialog.StringOption("Exec", desktopEntry.getExec()),
			uspOptionDialog.StringOption("Icon", desktopEntry.getIcon()),
			uspOptionDialog.BooleanOption("Terminal", desktopEntry.getTerminal())
		]
		newValues = dialog.run(values)
		if not newValues:
			return
		desktopEntry.set("Name", newValues[0])
		desktopEntry.set("GenericName", newValues[1])
		desktopEntry.set("Comment", newValues[2])
		desktopEntry.set("Exec", newValues[3])
		desktopEntry.set("Icon", newValues[4])
		desktopEntry.set("Terminal", newValues[5])
		desktopEntry.write()
		self.updatePage(self.currentPageIndex)
		
	def menuRemoveEntry(self, widget, position):
		tasks = self.tasksList.readList()
		task = tasks[self.currentPageIndex]
		task.removeDesktopEntryFileNames(position)
		self.tasksList.writeList(tasks)
		self.updatePage(self.currentPageIndex)
	
	def onTabAddClicked(self, widget):
		dialog = uspOptionDialog.OptionDialog("Add task..", self.getToplevelWindow())
		returnValue = dialog.run([uspOptionDialog.StringOption("New task name", "")])
		if not returnValue:
			return
		taskName = returnValue[0]
		task = Task(taskName)
		tasks = self.tasksList.readList()
		tasks.append(task)
		self.tasksList.writeList(tasks)
		self.createPage(task)
	
	def onTabRemoveClicked(self, widget):
		tasks = self.tasksList.readList()
		del(tasks[self.currentPageIndex])
		self.tasksList.writeList(tasks)
		self.noteBook.remove_page(self.currentPageIndex)
	
	def onTabEditClicked(self, widget):
		tasks = self.tasksList.readList()
		task = tasks[self.currentPageIndex]
		dialog = uspOptionDialog.OptionDialog("Edit task..", self.getToplevelWindow())
		options = [
			uspOptionDialog.StringOption("Name", task.getName()),
			uspOptionDialog.IntegerOption("Number of columns", task.getNumberOfColumns(), 1, 10, 1)
		]
			
		returnValue = dialog.run(options)
		if not returnValue:
			return
		taskName, numberOfColumns = returnValue
		
		task.setName(taskName)
		task.setNumberOfColumns(numberOfColumns)
		self.tasksList.writeList(tasks)
		
		currentPage = self.noteBook.get_nth_page(self.currentPageIndex)
		tabWidget = self.noteBook.get_tab_label(currentPage)
		tabWidget.get_children()[0].set_text(taskName)
		self.updatePage(self.currentPageIndex)
		
	def onPageSwitched(self, notebook, page, pageIndex):
		self.currentPageIndex = pageIndex
	
	def onPageReordered(self, notebook, page, newPageIndex):
		source = newPageIndex
		dest = self.currentPageIndex
		tasks = self.tasksList.readList()
		tasks[source],tasks[dest] = tasks[dest],tasks[source]
		self.tasksList.writeList(tasks)
		self.currentPageIndex = newPageIndex
	
	def onDragBegin(self, widget, dragContext):
		self.dragging = True
		self.dragBeginCurrentPageIndex = self.currentPageIndex
		
	def onDragEnd(self, widget, dragContext):
		self.dragging = False
		
	def onDragDataSend(self, widget, context, selection, targetType, time, fileUri):
		selection.set_text(str(self.dragBeginCurrentPageIndex)+" "+str(widget.position))
		
	def onDragDataRecieved(self, widget, context, x, y, selection, targetType, time):
		if targetType == 0:
			sourceWidget = context.get_source_widget()
			if sourceWidget:
				print "source:", sourceWidget, "target:", widget
				print  selection.get_text().split()
				
			if isinstance(widget, gtk.ScrolledWindow):
				sourceTaskIndex, sourceEntryIndex = selection.get_text().split()
				sourceTaskIndex, sourceEntryIndex = int(sourceTaskIndex), int(sourceEntryIndex)
				
				targetTaskIndex = self.currentPageIndex
				
				tasks = self.tasksList.readList()
				sourceTask = tasks[sourceTaskIndex]
				targetTask = tasks[targetTaskIndex]
				
				sourceEntry = sourceTask.desktopEntryFileNames[sourceEntryIndex]
				targetTask.desktopEntryFileNames.append(sourceEntry)
				del(sourceTask.desktopEntryFileNames[sourceEntryIndex])
				
				self.tasksList.writeList(tasks)
				
				self.updatePage(sourceTaskIndex)
				self.updatePage(targetTaskIndex)
				
			elif isinstance(widget, gtk.Button):
				sourceTaskIndex, sourceEntryIndex = selection.get_text().split()
				sourceTaskIndex, sourceEntryIndex = int(sourceTaskIndex), int(sourceEntryIndex)
				
				targetTaskIndex, targetEntryIndex = self.currentPageIndex, widget.position
				
				tasks = self.tasksList.readList()
				sourceTask = tasks[sourceTaskIndex]
				targetTask = tasks[targetTaskIndex]
				
				sourceEntry = sourceTask.desktopEntryFileNames[sourceEntryIndex]
				targetEntry = targetTask.desktopEntryFileNames[targetEntryIndex]
				
				sourceTask.desktopEntryFileNames[sourceEntryIndex] = targetEntry
				targetTask.desktopEntryFileNames[targetEntryIndex] = sourceEntry
				
				self.tasksList.writeList(tasks)
				
				self.updatePage(sourceTaskIndex)
				self.updatePage(targetTaskIndex)
			
		else:
			for uri in selection.get_uris():
				scheme = gnomevfs.get_uri_scheme(uri)
				if scheme == "file":
					mime = gnomevfs.get_mime_type(uri)
					if mime == "application/x-desktop":
						path = gnomevfs.get_local_path_from_uri(uri)
						entry = DesktopEntry(path)
						entryName = os.path.basename(entry.getFileName())
						entry.write(self.favouritesFolder + "/" + entryName)
						
						tasks = self.tasksList.readList()
						task = tasks[self.currentPageIndex]
						filenames = task.getDesktopEntryFileNames()
						
						if entryName in filenames:
							return
						if hasattr(widget, "position"):
							filenames.insert(widget.position, entryName)
						else:
							filenames.append(entryName)
						self.tasksList.writeList(tasks)
						self.updatePage(self.currentPageIndex)
					else:
						name = gnomevfs.get_file_info(uri).name
						icon = self.iconFactory.iconNameForURI(uri, mime)
						description = gnomevfs.mime_get_description(mime)
						
						fileName = "file-"+name+".desktop"
						filePath = self.favouritesFolder+"/"+fileName
						
						entry = DesktopEntry(filePath)
						entry.set("Name", name)
						entry.set("Comment", description)
						entry.set("Icon", icon)
						entry.set("Exec", "xdg-open "+uri)
						entry.write(filePath)
						
						tasks = self.tasksList.readList()
						task = tasks[self.currentPageIndex]
						filenames = task.getDesktopEntryFileNames()
						
						if hasattr(widget, "position"):
							filenames.insert(widget.position, fileName)
						else:
							filenames.append(fileName)
						self.tasksList.writeList(tasks)
						self.updatePage(self.currentPageIndex)
				elif scheme == "http":
					print "Http scheme: " + uri
			if len(selection.get_uris()) == 0:
				text = selection.get_text()
				if text != None and text != "":
					if text.startswith("http://"):
						forName = text.strip('./?=-:\\')
						fileName = "http-"+forName+".desktop"
						filePath = self.favouritesFolder+"/"+fileName
						
						entry = DesktopEntry(filePath)
						entry.set("Name", text)
						entry.set("Comment", text)
						entry.set("Icon", 'html')
						entry.set("Exec", "xdg-open "+text)
						entry.write(filePath)
						
						tasks = self.tasksList.readList()
						task = tasks[self.currentPageIndex]
						filenames = task.getDesktopEntryFileNames()
						
						if hasattr(widget, "position"):
							filenames.insert(widget.position, fileName)
						else:
							filenames.append(fileName)
						self.tasksList.writeList(tasks)
						self.updatePage(self.currentPageIndex)

class NewFavouritesView(object):
	def __init__(self, widgetTree, mainEventWindow):
		self.widgetTree = widgetTree
		self.mainEventWindow = mainEventWindow
		mainNoteBookWidget = self.widgetTree.get_widget("MainNotebook1")
		self.mainNoteBook = MainNoteBookView(mainNoteBookWidget, widgetTree, mainEventWindow)
		

class Configuration(uspCoreUtils.Singleton):
	def __init__(self):
		uspCoreUtils.Singleton.__init__(self)
	
	def hideView(self):
		if self.favouritesGClient.getBoolean('hide_on_application_start', True):
			self.favouritesMainWidget.hideView()
			
class UspPlugin(object):
	gladeFilename = "favourites.glade"
	
	def __init__(self):
		self.gladefile = os.path.join(os.path.dirname(__file__) , self.gladeFilename)
		
		if not os.path.exists(self.gladefile):
			raise Exception("Glade file '"+ self.gladeFilename +"' does not exist.")
			
		self.widgetTree = gtk.glade.XML(self.gladefile, "MainWindow")
		self.window = self.widgetTree.get_widget("MainWindow")
		self.contentHolder = self.widgetTree.get_widget("MainEventBox")
		
		self.gClient = uspGConfig.GConfigurator('/apps/usp2/plugins/favourites')
		Configuration().favouritesGClient = self.gClient
		Configuration().favouritesMainWidget = self
		Configuration().opacity = 0.8
		
		self.applicationView = NewFavouritesView(self.widgetTree, self.contentHolder)
		
	def pluginName(self):
		return "Favourites"
	
	def mainWidget(self):
		return self.contentHolder
	
	def pluginIcon(self):
		return 'package_settings'
	
	def getPluginSize(self):
		width = self.gClient.getInteger('width', 500)
		height = self.gClient.getInteger('height', 250)
		return width, height
	
	def setPluginSize(self, width, height):
		self.gClient.setInteger('width', width)
		self.gClient.setInteger('height', height)
	
	def hideView(self):
		if hasattr(self, "configuration"):
			self.configuration.hide()
	
	def widgetsToApplyLowOpacity(self):
		pass
		
	def widgetsToApplyHighOpacity(self):
		pass
